package com.tfleo.mealssenger;

import com.tfleo.mealssenger.adapter.PlaceListAdapter;
import com.tfleo.mealssenger.balloon.CustomOverlayItem;
import com.tfleo.mealssenger.balloon.PlacesOverlay;
import com.tfleo.mealssenger.object.Place;
import com.tfleo.mealssenger.utility.DataFetcher;
import com.tfleo.mealssenger.utility.LocationTools;
import com.tfleo.mealssenger.utility.NetworkTools;
import com.tfleo.mealssenger.R;

import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Point;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.location.LocationProvider;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.Toast;
import android.widget.ViewSwitcher;

import com.google.android.maps.GeoPoint;
import com.google.android.maps.MapActivity;
import com.google.android.maps.MapController;
import com.google.android.maps.MapView;
import com.google.android.maps.Overlay;

import java.util.ArrayList;
import java.util.List;

/**
 * Extension of MapActivity to show user's location
 * and nearby location on Google Map.
 *
 * @author Theodore Felix Leo
 */
public class MapBrowseActivity extends MapActivity {
	private static final int MIN_TIME_TO_UPDATE = 1000 * 300; //in msec
	private static final int MIN_DIST_TO_UPDATE = 300; //in meters
	public static final int ADD_PLACE = Menu.FIRST;
	public static final int REFRESH = Menu.FIRST + 1;
	public static final int ACTIVITY_ADD_PLACE = 1;

	private MapView vMap;
	private MapController mMapController;
	
	private ViewSwitcher vSwitcher;
	private Button vFilterBtn;
	private Button vSwitchViewBtn;
	private ListView vPlaceList;

	private List<Overlay> mMapOverlays;
	private PlacesOverlay mPlacesOverlay;
	private MapOverlay mFocusOverlay;

	private LocationManager mLocationManager;
    private LocationListener mLocationListener;

    private String mProvider = LocationManager.GPS_PROVIDER;
    protected Location currentBestLocation;

    private ArrayList<Place> mPlaces;
    private ImageView mCurrentMarker;
    private int mCurrentView = 1;
    
    private Context mContext;
    private LayoutInflater mInflater;
    private PlaceListAdapter mListAdapter;
    
    private GeoPoint mPos;
    private double mFocusLatitude, mFocusLongitude;
    private GeoPoint mFocusPos;
    private boolean start = true;
    private int mSortBy;
    private String mFilterBy;
    private boolean isFiltered = false;

	@Override
	public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.map_browse_layout);
        mContext = this;
        mInflater = (LayoutInflater) getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
        vSwitcher = (ViewSwitcher) findViewById(R.id.switcher);

        //Get location from GPS or NETWORK
        mLocationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
        mLocationListener = new MyLocationListener();
        
        mPlaces = new ArrayList<Place>();

        if(!NetworkTools.isNetworkAvailable(mContext)) {
			Toast.makeText(getApplicationContext(),
					"Network is currently unavailable", Toast.LENGTH_LONG).show();
		}

        if(!mLocationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)) {
        	mProvider = LocationManager.NETWORK_PROVIDER;
        	//Toast.makeText(getApplicationContext(), "Switching provider", Toast.LENGTH_SHORT).show();
        }

        mLocationManager.requestLocationUpdates(mProvider,
        		MIN_TIME_TO_UPDATE,
        		MIN_DIST_TO_UPDATE,
        		mLocationListener);

        vMap = (MapView) findViewById(R.id.map_view);
        vMap.setBuiltInZoomControls(true);
        mMapController = vMap.getController();

        mMapOverlays = vMap.getOverlays();
        mPlacesOverlay = new PlacesOverlay(this.getResources()
        		.getDrawable(R.drawable.place_marker), vMap);

        showCurrentLocation();
       
        vFilterBtn = (Button) findViewById(R.id.filter_btn);
        vFilterBtn.setOnClickListener(new OnClickListener() {
			public void onClick(View v) {
				//addPlace();
				final CharSequence[] items = {"None", "Bar", "Cafe", "Dessert", "Fastfood", "Foodcourt", "Hawker", 
						"Restaurant", "Snacks"};

		        AlertDialog.Builder builder = new AlertDialog.Builder(mContext);
		        builder.setTitle("Filter by...");
		        builder.setSingleChoiceItems(items, -1, new DialogInterface.OnClickListener() {
		            public void onClick(DialogInterface dialog, int item) {
		                //Toast.makeText(getApplicationContext(), items[item], Toast.LENGTH_SHORT).show();
		            	mFilterBy = (String) items[item];
		            	if(item == 0) {
		            		isFiltered = false;
		            	} else {
		            		isFiltered = true;
		            	}
		            }
		        });
		        builder.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
		            public void onClick(DialogInterface dialog, int id) {
		            	FetchNearbyTask task = new FetchNearbyTask();
		    			task.execute(currentBestLocation.getLongitude(), currentBestLocation.getLatitude());
		                dialog.dismiss();
		            }
		        });
		        
		        AlertDialog alert = builder.create();
		        alert.show();
			}
        });
        
        vPlaceList = (ListView) findViewById(R.id.list_view);
        
        vSwitchViewBtn = (Button) findViewById(R.id.switch_view_btn);
        vSwitchViewBtn.setOnClickListener(new OnClickListener() {
			public void onClick(View v) {
				switch(mCurrentView) {
				case 1:
					mCurrentView = 2;
					vSwitcher.showNext();
			
					/*mListAdapter = new PlaceListAdapter(mContext, R.layout.map_browse_list_entry, 
							R.id.name, mPlaces, mInflater);
					vPlaceList = (ListView) findViewById(R.id.list_view);
			        vPlaceList.setAdapter(mListAdapter);*/
					
					mLocationManager.removeUpdates(mLocationListener);
					break;
				case 2:
					mCurrentView = 1;
					vSwitcher.showPrevious();
					mLocationManager.requestLocationUpdates(mProvider,
			        		MIN_TIME_TO_UPDATE,
			        		MIN_DIST_TO_UPDATE,
			        		mLocationListener);
					break;
				default:
					mCurrentView = 1;	
				}
			}
        });
    
        
	}

	@Override
	protected void onResume() {
		super.onResume();

		if(!NetworkTools.isNetworkAvailable(mContext)) {
			Toast.makeText(getApplicationContext(),
					"Network is currently unavailable", Toast.LENGTH_LONG).show();
		}

		/*mLocationManager.requestLocationUpdates(mProvider,
				MIN_TIME_TO_UPDATE,
				MIN_DIST_TO_UPDATE,
				mLocationListener);*/
	}

	@Override
	protected void onPause() {
		super.onPause();
		mLocationManager.removeUpdates(mLocationListener);
	}

	@Override
	protected boolean isRouteDisplayed() {
		return false;
	}

	/**
	 * Show current reading of location and check last known location
	 * to determine whether it is still relevant.
	 * 
	 * Update to new location if last known location is irrelevant.
	 */
	protected void showCurrentLocation() {
		Location location = mLocationManager.getLastKnownLocation(mProvider);

		if(location != null) {
			if(LocationTools.isBetterLocation(location, currentBestLocation)) {
				currentBestLocation = location;
			} else {
				mLocationManager.requestSingleUpdate(mProvider, mLocationListener, null);
			}
			updateLocation(currentBestLocation);
		}
	}

	/**
	 * Update the map marker to show current location.
	 *
	 * @param location The new location to be shown
	 */
	protected void updateLocation(Location location) {
		//mPlaces.clear();
		
		mPos = new GeoPoint(
				(int) (location.getLatitude() * 1E6),
				(int) (location.getLongitude() * 1E6));
		
		if(mFocusPos == null || start) {
			mFocusPos = new GeoPoint(
					(int) (location.getLatitude() * 1E6),
					(int) (location.getLongitude() * 1E6));
			start = false;
		}
		
		MapView.LayoutParams lp = new MapView.LayoutParams(
				MapView.LayoutParams.WRAP_CONTENT,
				MapView.LayoutParams.WRAP_CONTENT,
				mPos,
				MapView.LayoutParams.CENTER);

		//Set new marker if it is not yet set
		if(mCurrentMarker != null) {
			mCurrentMarker.setLayoutParams(lp);
		} else {
			mCurrentMarker = new ImageView(this);
			mCurrentMarker.setImageResource(R.drawable.my_marker);
			vMap.addView(mCurrentMarker, lp);
		}

		
		
		if(!NetworkTools.isNetworkAvailable(mContext)) {
			Toast.makeText(getApplicationContext(),
					"Network is currently unavailable", Toast.LENGTH_LONG).show();
		} else {
			FetchNearbyTask task = new FetchNearbyTask();
			task.execute(location.getLongitude(), location.getLatitude());
		}
	}
	
	private class FetchNearbyTask extends AsyncTask<Double, Void, Boolean> {
		@Override
		protected Boolean doInBackground(Double... point) {
			/*if(point[2] == 1.0) {
				mPlaces = DataFetcher.fetchNearby(point[0], point[1], mSortBy, true, mFilterBy);
			} else {
				mPlaces = DataFetcher.fetchNearby(point[0], point[1], mSortBy, false, mFilterBy);
			}*/
			mPlaces = DataFetcher.fetchNearby(point[0], point[1], mSortBy, isFiltered, mFilterBy);
			if(mPlaces == null) {
				return false;
			}

			return true;
		}

		@Override
		protected void onPostExecute(Boolean result) {
			if(result) {
				
				//Clear container
				mMapOverlays.clear();
				mPlacesOverlay.clear();
				
				mFocusOverlay = new MapOverlay();
				mMapOverlays.add(mFocusOverlay);
				
				for(int i = 0; i < mPlaces.size(); i++) {
					Place p = mPlaces.get(i);

					float latitude = p.latitude;
					float longitude = p.longitude;
					
					Location target = new Location(LocationManager.NETWORK_PROVIDER);
					target.setLatitude(latitude);
					target.setLongitude(longitude);
					
					p.setDistance((int)(currentBestLocation.distanceTo(target)));
		
					GeoPoint otherPos = new GeoPoint(
							(int) (latitude * 1E6),
							(int) (longitude * 1E6));
					CustomOverlayItem thisOverlay = new CustomOverlayItem(otherPos, p.id,
							p.name, p.description, p.category, false);
		
					mPlacesOverlay.addOverlay(thisOverlay);
					mMapOverlays.add(mPlacesOverlay);
				}
				mMapController.animateTo(mFocusPos);
				mMapController.setZoom(17);
				vMap.invalidate();
				
				mListAdapter = new PlaceListAdapter(mContext, R.layout.map_browse_list_entry, 
						R.id.name, mPlaces, mInflater);
				vPlaceList.setAdapter(mListAdapter);
				
			} else {
				Toast.makeText(mContext, "Error: Connection with server failed!", Toast.LENGTH_SHORT).show();
			}
		}
	}
	
	/**
	 * Listener class to handle changes in location and provider.
	 */
	public class MyLocationListener implements LocationListener {
		public void onLocationChanged(Location location) {
			if(location != null) {
				if(LocationTools.isBetterLocation(location, currentBestLocation)) {
					currentBestLocation = location;
				}
				updateLocation(currentBestLocation);
			}
		}

		public void onProviderDisabled(String provider) {
			mProvider = (provider == LocationManager.GPS_PROVIDER) ?
					LocationManager.NETWORK_PROVIDER : LocationManager.GPS_PROVIDER;
			Toast.makeText(getApplicationContext(), "Switching provider...", Toast.LENGTH_SHORT).show();
		}

		public void onProviderEnabled(String provider) {
			mProvider = (provider == LocationManager.GPS_PROVIDER) ?
					LocationManager.GPS_PROVIDER : LocationManager.NETWORK_PROVIDER;
			Toast.makeText(getApplicationContext(), "Switching provider...", Toast.LENGTH_SHORT).show();
		}

		public void onStatusChanged(String provider, int status, Bundle extras) {
			if(status != LocationProvider.AVAILABLE) {
				mProvider = (provider == LocationManager.GPS_PROVIDER) ?
						LocationManager.NETWORK_PROVIDER : LocationManager.GPS_PROVIDER;
			}
			Toast.makeText(getApplicationContext(), "Switching provider...", Toast.LENGTH_SHORT).show();
		}
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		boolean result = super.onCreateOptionsMenu(menu);
		MenuInflater inflater = getMenuInflater();
		inflater.inflate(R.menu.map_menu, menu);
		return result;
	}

	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		Intent i;
		switch(item.getItemId()) {
		case R.id.add_place:
			addPlace();
			return true;
		case R.id.refresh:
			if(!NetworkTools.isNetworkAvailable(mContext)) {
				Toast.makeText(getApplicationContext(),
						"Network is currently unavailable", Toast.LENGTH_SHORT).show();
			} else {
				start = true;
				showCurrentLocation();
			}
			return true;
		case R.id.sort:
			final CharSequence[] items = {"Distance", "Rating"};

	        AlertDialog.Builder builder = new AlertDialog.Builder(mContext);
	        builder.setTitle("Sort by...");
	        builder.setSingleChoiceItems(items, mSortBy, new DialogInterface.OnClickListener() {
	            public void onClick(DialogInterface dialog, int item) {
	                //Toast.makeText(getApplicationContext(), items[item], Toast.LENGTH_SHORT).show();
	            	mSortBy = item;
	            }
	        });
	        builder.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
	            public void onClick(DialogInterface dialog, int id) {
	            	/*if(!filter) {
	            		FetchNearbyTask task = new FetchNearbyTask();
		    			task.execute(currentBestLocation.getLongitude(), currentBestLocation.getLatitude(), 0.0);
	            	} else {
		            	FetchNearbyTask task = new FetchNearbyTask();
		    			task.execute(currentBestLocation.getLongitude(), currentBestLocation.getLatitude(), 1.0);
	            	}*/
	            	FetchNearbyTask task = new FetchNearbyTask();
	    			task.execute(currentBestLocation.getLongitude(), currentBestLocation.getLatitude());
	                dialog.dismiss();
	            }
	        });
	        
	        AlertDialog alert = builder.create();
	        alert.show();
			return true;
		case R.id.preferences:
			showPreferences();
			return true;
		case R.id.info:
			showInfo();
			return true;
		case R.id.logout:
			i = new Intent(this, LoginActivity.class);
			i.putExtra("logout", true);
			startActivity(i);
			finish();
			return true;
		}

		return super.onOptionsItemSelected(item);
	}

	/**
	 * Start AddPlaceActivity.
	 */
	private void addPlace() {
		Intent i = new Intent(this, AddPlaceActivity.class);
		startActivityForResult(i, ACTIVITY_ADD_PLACE);
	}

	/**
	 * Start SettingsActivity.
	 */
	private void showPreferences() {
		Intent i = new Intent(this, SettingsActivity.class);
		startActivityForResult(i, ACTIVITY_ADD_PLACE);
	}

	/**
	 * Start InfoActivity.
	 */
	private void showInfo() {
		Intent i = new Intent(this, InfoActivity.class);
		startActivity(i);
	}

	@Override
	protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
		super.onActivityResult(requestCode, resultCode, intent);
		//TODO: put new location on map
		//Toast.makeText(getApplicationContext(), "Places Added", Toast.LENGTH_SHORT).show();
	}

	/**
	 * Extension of Overlay class to handle tap on map and draw a marker
	 * on the tap location
	 */
	class MapOverlay extends com.google.android.maps.Overlay {
		boolean isPinch;
		boolean isLongPress;
		@Override
		public boolean draw(Canvas canvas, MapView mapView, boolean shadow, long when) {
			super.draw(canvas, mapView, shadow);                   

			//Translate the GeoPoint to screen pixels
			Point screenPts = new Point();
			mapView.getProjection().toPixels(mFocusPos, screenPts);

			//Add marker
			Bitmap bmp = BitmapFactory.decodeResource(
					getResources(), R.drawable.star_marker);            
			canvas.drawBitmap(bmp, screenPts.x - (float)21, screenPts.y - (float)21, null);         
			return false;
		}

		@Override
		public boolean onTap(GeoPoint p, MapView mapView) {   
			if (isPinch){
				return false;
			} else if(isLongPress){
				mFocusPos = p;
				mFocusLatitude = mFocusPos.getLatitudeE6()/1E6;
				mFocusLongitude = mFocusPos.getLongitudeE6()/1E6;
				
				FetchNearbyTask task = new FetchNearbyTask();
				task.execute(mFocusLongitude, mFocusLatitude);

				return true;
			}
			return false;
		}

		@Override
		public boolean onTouchEvent(MotionEvent e, MapView mapView) {
			int fingers = e.getPointerCount();
			long duration = e.getEventTime();
			if(e.getAction() == MotionEvent.ACTION_DOWN && duration > 1000 ){
				isPinch = false;  // Touch DOWN, don't know if it's a pinch yet
				isLongPress = true;
			}
			if(e.getAction() == MotionEvent.ACTION_MOVE && fingers == 2 ){
				isPinch = true;   // Two fingers, def a pinch
				isLongPress = false;
			}
			return super.onTouchEvent(e,mapView);
		}

	}
}
